<template>
  <div style="display:flex;position: relative;">
    <div ref="stateDom" />
    <div class="buttonFlex">
      <button @click="startButton">开始</button>
      <button @click="stopButton">暂停</button>

    </div>
  </div>
</template>
<script setup>
// 导入gui
import * as dat from 'dat.gui'
// 导入Vue组合API
import { onMounted, reactive, ref, toRefs, toRaw, onUnmounted } from 'vue'
// 导入threejs
import * as THREE from 'three'
// 导入OrbitControls控件 用来拖拽和放大缩小模型
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
// 声明需要的参数
const content = reactive({
  // 宽高
  width: window.innerWidth,
  height: window.innerHeight,
  // 相机的渲染尺寸
  cameraSize: 200,
  // 声明场景对象Scene
  scene: null,
  // 声明网格模型mesh
  mesh: null,
  // 声明相机camera
  camera: null,
  // 创建渲染器对象
  renderer: null,
  // 创建动画
  animation: null,
  // 材质
  skyBoxMaterial: null,
  // 动画速度
  speedX: 0.01,
  gui: null
})

// 获取元素的Dom
const stateDom = ref(null)
// 结构
const { width } = toRefs(content)
//! 开始threejs的渲染步骤
const box = () => {
  console.log(width.value)
  //! 1. 创建场景对象Scene
  content.scene = new THREE.Scene()
  // 把创建场景对象 转换为普通对象格式
  const scene = toRaw(content.scene)
  //!  2. 创建网格模型
  const geometry = new THREE.BoxGeometry(100, 100, 100) // 创建一个立方体几何对象Geometry
  // 获取图片网格材质
  const texture = new THREE.TextureLoader().load('https://jinyanlong-1305883696.cos.ap-hongkong.myqcloud.com/%E5%B0%8Fmao.jpg')
  // 使用网格材质
  content.skyBoxMaterial = new THREE.MeshBasicMaterial({
    map: texture,
    side: THREE.DoubleSide
  })
  // 网格模型对象Mesh 导入网格模型和网格才是
  content.mesh = new THREE.Mesh(geometry, content.skyBoxMaterial)
  // 把创建网格模型对象 转换为普通对象格式
  const mesh = toRaw(content.mesh)
  scene.add(mesh) // 网格模型添加到场景中
  //! 3. 光源设置
  // 平行光1
  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.4)
  directionalLight.position.set(400, 200, 300)
  scene.add(directionalLight)
  // 环境光
  const ambient = new THREE.AmbientLight(0xffffff, 0.6)
  scene.add(ambient)
  // width和height用来设置Three.js输出Canvas画布尺寸，同时用来辅助设置相机渲染范围
  //! 4. 相机设置
  const k = content.width / content.height // Three.js输出的Canvas画布宽高比
  const s = content.cameraSize // 控制相机渲染空间左右上下渲染范围，s越大，相机渲染范围越小
  // THREE.OrthographicCamera()创建一个正投影相机对象
  // -s * k, s * k, s, -s, 1, 1000定义了一个长方体渲染空间，渲染空间外的模型不会被渲染
  content.camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000)
  content.camera.position.set(190, 0, 366) // 相机在Three.js坐标系中的位置

  content.camera.lookAt(0, 0, 0) // 相机指向Three.js坐标系原点
  //! 5. 创建渲染器对象
  content.renderer = new THREE.WebGLRenderer({
    antialias: true // 开启锯齿
  })
  content.renderer.setPixelRatio(window.devicePixelRatio)// 设置设备像素比率,防止Canvas画布输出模糊。
  content.renderer.setSize(content.width, content.height) // 设置渲染区域尺寸
  content.renderer.setClearColor(0x00000, 1) // 设置背景颜色
  //  domElement表示Three.js渲染结果,也就是一个HTML元素(Canvas画布)
  stateDom.value.appendChild(content.renderer.domElement) // body元素中插入canvas画布
  // 执行渲染操作   指定场景、相机作为参数
  content.renderer.render(scene, content.camera)
  // 启动动画
  startAnimation()
  // 随页面尺寸进行渲染
  updateCamera()
}

// 使用图形化调试
const guiAdd = () => {
  // 启动图形化调试
  content.gui = new dat.GUI({ closed: true })
  const { gui } = content
  // 把创建网格模型对象 转换为普通对象格式
  const mesh = toRaw(content.mesh)
  gui.add(mesh.position, 'y', -100, 100, 0.01).name('Y轴切换')
  gui.add(mesh.position, 'x').min(-3).max(3).step(0.01).name('X轴切换')
  gui.add(mesh, 'visible').name('是否显示模型')
  gui.add(content.skyBoxMaterial, 'wireframe').name('显示线框')
  // 设置暂停动画
  const end = {
    color: 0xff0000,
    spin: () => {
      // 暂停动画
      content.speedX = 0
    }
  }
  gui.add(end, 'spin').name('暂停动画')
  // 设置开始动画
  const start = {
    color: 0xff0000,
    spin: () => {
      // 开始动画
      startButton()
    }
  }
  gui.add(start, 'spin').name('开始动画')
}
// 销毁gui
const colseGui = () => {
  const { gui } = content
  gui.hide()
}
// 使用three.js 轨道控制器
const OrbitControlsF = () => {
  // 使用轨道控制器
  // 创建控件对象  控件可以监听鼠标的变化，改变相机对象的属性
  // 旋转：拖动鼠标左键
  // 缩放：滚动鼠标中键
  // 平移：拖动鼠标右键
  const controls = new OrbitControls(content.camera, content.renderer.domElement)
  controls.enableDamping = true
}
// 执行动画
const startAnimation = () => {
  // 获取场景对象
  const scene = toRaw(content.scene)
  // 获取网格对象
  const mesh = toRaw(content.mesh)
  // 旋转起来
  mesh.rotateY(content.speedX)
  // 进行渲染
  content.renderer.render(scene, content.camera)
  requestAnimationFrame(startAnimation)
}
// 随页面尺寸进行渲染
const updateCamera = () => {
  // 监听页面尺寸是否修改
  window.onresize = () => {
    // 获取新的尺寸数据
    content.width = window.innerWidth
    content.height = window.innerHeight
    // 重新渲染场景
    content.renderer.setSize(content.width, content.height)
    // 更新相机参数 不更新相机参数 会导致相机内容拉伸
    const k = content.width / content.height // Three.js输出的Canvas画布宽高比
    const s = content.cameraSize// 控制相机渲染空间左右上下渲染范围，s越大，相机渲染范围越大
    content.camera.left = -s * k
    content.camera.right = s * k
    content.camera.top = s
    content.camera.bottom = -s
    // 更新相机参数后执行
    content.camera.updateProjectionMatrix()
  }
}
// 开始按钮
const startButton = () => {
  // 开始旋转0.1
  content.speedX = 0.01
}
// 暂停按钮
const stopButton = () => {
  // 暂停
  content.speedX = 0
}
// 销毁
onUnmounted(() => {
  colseGui()
  // 把创建场景对象 转换为普通对象格式
  const scene = toRaw(content.scene)
  scene.remove()

  // context.messageTag.element.style.opacity = 0 // 显示标签
  // context.messageTag.element.style.opacity = 0 // 显示标签
})
onMounted(() => {
  // 渲染threejs的立体几何对象
  box()
  // // 启用坐标轴
  // coordinate()
  // 使用轨道控制器
  OrbitControlsF()
  // 启用js调试
  guiAdd()
})
</script>
<script>
export default {
  name: 'HomeView'
}
</script>
<style lang="scss" scoped>
.buttonFlex {
  position: absolute;
  top: 20px;
  left: 20px;
}
button + button {
  margin-left: 10px;
}
</style>
